package ru.papergames.common.form;

import org.springframework.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import ru.papergames.common.model.PcPlayer;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Alexander Troshanin
 */
public class PcPlayerForm extends AbstractPlayerForm {

    private static Log log = LogFactory.getLog(PcPlayerForm.class);

    public enum ConnectionState {
        CONNECTED_TO_BATTLE_SERVER,
        DISCONNECTED_FROM_BATTLE_SERVER,
        WAITING_FOR_GETTING_CONNECTED;
    }

    private int activeSessionsCount;

    private Lock lock = new ReentrantLock();
//    private boolean connectedToBattleServer;

    private ConnectionState connectionState = ConnectionState.WAITING_FOR_GETTING_CONNECTED;

    public PcPlayerForm() {
    }

    public PcPlayerForm(PcPlayer pcPlayer) {
        super(pcPlayer);
    }

    public ConnectionState getConnectionState() {
        return connectionState;
    }

    public boolean isConnectedToBattleServer() {
        return connectionState == ConnectionState.CONNECTED_TO_BATTLE_SERVER;
    }

    public boolean isDisconnectedToBattleServer() {
        return connectionState == ConnectionState.DISCONNECTED_FROM_BATTLE_SERVER;
    }

    public boolean isWaitingForGettingConnected() {
        return connectionState == ConnectionState.WAITING_FOR_GETTING_CONNECTED;
    }

    public void setConnectedToBattleServer() {
        connectionState = ConnectionState.CONNECTED_TO_BATTLE_SERVER;
    }

    public void setDisconnectedToBattleServer() {
        connectionState = ConnectionState.DISCONNECTED_FROM_BATTLE_SERVER;
    }

    public void setWaitingForGettingConnected() {
        connectionState = ConnectionState.WAITING_FOR_GETTING_CONNECTED;
    }

    public int getActiveSessionsCount() {
        return activeSessionsCount;
    }

    public void setActiveSessionsCount(int activeSessionsCount) {
        this.activeSessionsCount = activeSessionsCount;
    }

    public void validate(List<String> errors) {

        if (!StringUtils.hasText(username) || !username.matches(".+@.+\\.[a-z]+")){
            errors.add("player.validation.bad.email");
        }
        if (!StringUtils.hasText(password) || password.length() < 6) {
            errors.add("player.validation.bad.password");
        }
        // todo: validate other fields.
    }

    private boolean tryLock(long time) {
        try {
            boolean res = lock.tryLock(time, TimeUnit.SECONDS);
            if (!res) {
                log.error("[ALARM]Failed to obtain lock on player: " + this);
            }
            return res;
        } catch (InterruptedException e) {
            return false;
        }
    }

    private void unlock() {
        lock.unlock();
    }

    public <T> ExecuteInLockResult<T> executeInLock(ExecutorInLock<T> exec) {
        ExecuteInLockResult<T> res = new ExecuteInLockResult<T>();
        if (!tryLock(5)) {
            return res;
        }
        res.executedInLock = true;
        try{
            res.value = exec.execute();
        } finally {
            unlock();
        }

        return res;
    }

    public interface ExecutorInLock<T> {
        T execute();
    }

    public class ExecuteInLockResult<T> {
        private boolean executedInLock;
        private T value;

        public boolean isExecutedInLock() {
            return executedInLock;
        }

        public T getValue() {
            return value;
        }
    }
}
